#pragma once

#include <future>
#include "../common/Message.hpp"
#include "../common/Net.hpp"

namespace rpc
{
    namespace client
    {
        class Requester
        {
        public:
            using ptr = std::shared_ptr<Requester>;
            using RequestCallback = std::function<void(BaseMessage::ptr&)>;
            using AsyncResponse = std::future<BaseMessage::ptr>;

            struct RequestDescribe
            {
                using ptr = std::shared_ptr<RequestDescribe>;
                BaseMessage::ptr request;
                REQ_TYPE req_type;
                std::promise<BaseMessage::ptr> response; // ASYNC 异步操作
                RequestCallback _req_callback;           // Callback 回调
            };

            // 收到相应后的回调函数
            void onResponse(const BaseConnection::ptr &conn, BaseMessage::ptr &rsp)
            {
                std::string id = rsp->id();
                auto it = _req_descs.find(id);
                if (it == _req_descs.end())
                {
                    ELOG("receive a response [%s] but no request", id.c_str());
                    return;
                }
                RequestDescribe::ptr rdp = it->second;

                if (rdp->req_type == REQ_TYPE::REQ_ASYNC)
                {
                    // rdp->response.get_future() = msg; // error
                    rdp->response.set_value(rsp); // 设置rsp,
                }
                else if (rdp->req_type == REQ_TYPE::REQ_CALLBACK)
                {
                    if (rdp->_req_callback)
                        rdp->_req_callback(rsp); 
                }
                else
                {
                    ELOG("unknown request type");
                }

                delDescribe(id); // 处理完一条请求后，将其删除
            }

            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, AsyncResponse &async_rsp)
            {
                RequestDescribe::ptr rdp = newDescribe(req, REQ_TYPE::REQ_ASYNC);
                if (rdp.get() == nullptr)
                {
                    ELOG("construct RequestDescribe failed");
                    return false;
                }
                conn->send(req); 
                // 阻塞，直到接收Rpc相应
                async_rsp = rdp->response.get_future();
                return true;
            }

            // 同步 -- 封装异步
            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, BaseMessage::ptr &rsp)
            {
                AsyncResponse async_rsp;
                // RequestDescribe::ptr rdp = newDescribe(req, REQ_TYPE::REQ_ASYNC, async_rs);
                bool n = send(conn, req, async_rsp);
                if (n == false)
                {
                    return false;
                }
                rsp = async_rsp.get();
                return true;
            }

            // bool send(const BaseConnection::ptr &conn, BaseMessage::ptr &req, BaseMessage::ptr& rsp)
            // {
            //     RequestDescribe::ptr rdp = newDescribe(req, );
            //     if (rdp.get() == nullptr)
            //     {
            //         ELOG("construct RequestDescribe failed");
            //         return false;
            //     }
            //     rdp->response.set_value(rsp);
            //     conn->send(req);
                
            //     return true;
            // }

            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, const RequestCallback &req_cb)
            {
                RequestDescribe::ptr rdp = newDescribe(req, REQ_TYPE::REQ_CALLBACK, req_cb);
                if (rdp.get() == nullptr)
                {
                    ELOG("construct RequestDescribe failed");
                    return false;
                }
                conn->send(req);
                return true;
            }

        private:
            RequestDescribe::ptr newDescribe(const BaseMessage::ptr &req, REQ_TYPE type, RequestCallback req_cb = RequestCallback())
            {
                std::unique_lock<std::mutex> lock(_mutex);
                RequestDescribe::ptr rdp = std::make_shared<RequestDescribe>();
                rdp->request = req;
                rdp->req_type = type;

                if (type == REQ_TYPE::REQ_CALLBACK && req_cb)
                    rdp->_req_callback = req_cb;

                _req_descs.insert(std::make_pair(rdp->request->id(), rdp));
                return rdp;
            }

            RequestDescribe::ptr getDescribe(const std::string &id)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _req_descs.find(id);
                if (it == _req_descs.end())
                {
                    return RequestDescribe::ptr();
                }

                return it->second;
            }

            void delDescribe(const std::string &id)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _req_descs.erase(id);
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, RequestDescribe::ptr> _req_descs; // id, desc
        };
    }
}